// stroke_extraction.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#include <vector>
#include <map>
#include <utility>
#include <sstream>
#include <opencv.hpp>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

#include "Ambigous_Zone_Based_Extraction.h"
#include "StrokeBuilder.h"

using namespace std;

int main(int argc, char* argv[])
{
	if (argc < 2)
	{
		cerr << "Wrong input!" << endl;
		//return 1;
	}
	char* input_filename = argv[1]; //"D:\\PhD\\Programs\\stroke_extraction\\Release\\test.png";
	
	vector<StrokeNode*> stroke_graph;
	IplImage* ambigous_zones_map;

	Ambigous_Zone_Based_Extraction *extractor = new Ambigous_Zone_Based_Extraction();
	extractor->run(input_filename, stroke_graph, &ambigous_zones_map);
	delete extractor;

	cvSaveImage("ambigous_zones_map.png",ambigous_zones_map);

	// Get max stroke's index
	int substroke_index = 0;
	if (stroke_graph.size() > 0)
	{
		substroke_index = stroke_graph.back()->id + 1;
	}
	
	for (int i = 0; i< ambigous_zones_map->height; i++)
	{
		for (int j = 0; j< ambigous_zones_map->width; j++)
		{
			CvScalar pixel_color;
			pixel_color = cvGet2D(ambigous_zones_map,i,j);
			
			if (( pixel_color.val[0] == 0) && (pixel_color.val[1] == 0) && (pixel_color.val[2] == 0))
			{
				set<CvPoint,ImagePointComp> substroke;
				set<CvPoint,ImagePointComp>::iterator it;
				CvScalar combine_color;
				substroke = Tools::get_substroke_elements(cvPoint(j,i),ambigous_zones_map,pixel_color, combine_color);
				if (substroke.size() < 10)
				{
					unsigned int node_id = (combine_color.val[0] * 256 * 256 + combine_color.val[1] * 256 + combine_color.val[2]) / 50 - 1;
					for (int k = 0; k < (int) stroke_graph.size(); k++)
					{
						if ( stroke_graph[k]->id == node_id)
						{
							for (it = substroke.begin(); it != substroke.end(); it++)
							{
								stroke_graph[k]->elements.insert(cvPoint((*it).x, (*it).y));
								cvSet2D(ambigous_zones_map,(*it).y, (*it).x,combine_color);
							}
							break;
						}
					}
					continue;
				}
				unsigned int hehe = (unsigned int) ((substroke_index + 1) * 50);
				unsigned char r;
				unsigned char g;
				unsigned char b;
				r = (unsigned char) (((unsigned int)(hehe << 24)) >> 24);
				g = (unsigned char) (((unsigned int)(hehe << 16)) >> 24);
				b = (unsigned char) (((unsigned int)(hehe << 8)) >> 24);
				if ((r == 46) && (g == 153) && (b == 0))
				{
					cout << "substroke index 46 153 0: " << substroke_index << endl;
				}
				if ((r == 32) && (g == 153) && (b == 0))
				{
					cout << "substroke index 32 153 0: " << substroke_index << endl;
				}
				CvScalar paint_color = cvScalar(b * 1.0,g * 1.0,r* 1.0);
				for (it = substroke.begin(); it != substroke.end(); it++)
				{
					cvSet2D(ambigous_zones_map,(*it).y, (*it).x,paint_color);
				}

				StrokeNode* substroke_node = new StrokeNode();
			
				substroke_node->elements = substroke;
				substroke_node->isAmbigousZone = false;
				substroke_node->id = substroke_index;
				stroke_graph.push_back(substroke_node);
				substroke_index++;
				
			}

		}
	}

	for( int node_index = 0; node_index < (int) stroke_graph.size(); node_index++)
	{
		if(stroke_graph[node_index]->isAmbigousZone)
		{
			map<int,vector<CvPoint>> borders_map;
			map<int,vector<CvPoint>>::iterator map_it;
			set<CvPoint,ImagePointComp>::iterator it;
			
			for(it = stroke_graph[node_index]->elements.begin(); it != stroke_graph[node_index]->elements.end(); it++)
			{
				for (int i = -1; i< 2; i++)
				{
					for (int j = -1; j< 2; j++)
					{
						//if( (i + j == -1) || (i + j == 1))
						{
							CvScalar pixel_color = cvGet2D(ambigous_zones_map,(*it).y + i, (*it).x + j);
							unsigned int node_id = (pixel_color.val[0] * 256 * 256 + pixel_color.val[1] * 256 + pixel_color.val[2]) / 50 - 1;
							if ((node_id != (stroke_graph[node_index]->id)) && ((pixel_color.val[0] != 255) || (pixel_color.val[1] != 255) || (pixel_color.val[2] != 255) ) )
							{

								CvPoint p;
								p.x = (*it).x + j;
								p.y = (*it).y + i;
								if (!binary_search(borders_map[node_id].begin(), borders_map[node_id].end(),p,Tools::point_compare))
								{
									borders_map[node_id].push_back(p);
									sort(borders_map[node_id].begin(), borders_map[node_id].end(),Tools::point_compare);
								}
							}
						}
					}
				}
			}

			for(map_it = borders_map.begin(); map_it != borders_map.end(); map_it++)
			{
				int node_id = (*map_it).first;

				for( int idx = 0; idx < (int) stroke_graph.size(); idx++)
				{
					if (stroke_graph[idx]->id == node_id)
					{
						stroke_graph[node_index]->create_link(stroke_graph[idx]);
						stroke_graph[idx]->create_link(stroke_graph[node_index]);
						vector<vector<CvPoint>> segments = Tools::create_segments((*map_it).second);
						for (int segment_idx = 0; segment_idx < (int) segments.size(); segment_idx++)
						{
							vector<CvPoint> border_ends;
							if (segments[segment_idx].size() > 1)
							{
								for (int bi = 0; bi< (int)segments[segment_idx].size(); bi++)
								{	
									int count = 0;
									int count2 = 0;
									for (int i = -1; i< 2; i++)
									{
										for (int j = -1; j< 2; j++)
										{
											if ((i != 0) || (j != 0))
											{
												CvPoint p;
												p.x = segments[segment_idx][bi].x + j;
												p.y = segments[segment_idx][bi].y + i;
												if (binary_search(segments[segment_idx].begin(), segments[segment_idx].end(),p,Tools::point_compare))
												{
													count++; 
													if( (i + j == -1) || (i + j == 1))
													{
														count2++;
													}
												}
											}
										}
									}
									if (count == 1)
									{
										border_ends.push_back(segments[segment_idx][bi]);
									}
									else
									{
										if (count2 == 1)
										{
											border_ends.push_back(segments[segment_idx][bi]);
										}
									}
								}	
								
								if (border_ends.size() <= 0)
								{
									for (int bi = 0; bi< (int)segments[segment_idx].size(); bi++)
									{
										cout << "(" << segments[segment_idx][bi].x << "," << segments[segment_idx][bi].y << ") ; ";
									}
									cout << endl;
								}
								else
								{
									CvPoint p1 = border_ends.front();
									CvPoint p2 = border_ends.back();
								
									stroke_graph[idx]->create_starting_pointers(p1,p2,stroke_graph[node_index]);						
								}
							}	
						}
					}
				}
			}

		}
	}
	
	//for( int node_index = 0; node_index < (int) stroke_graph.size(); node_index++)
	//{
	//	CvPoint p1 = stroke_graph[node_index]->gravity_center();
	//	for (int neighbour_idx = 0; neighbour_idx < (int) stroke_graph[node_index]->neighbours.size(); neighbour_idx++)
	//	{
	//		CvPoint p2 = stroke_graph[node_index]->neighbours[neighbour_idx]->gravity_center();
	//		cvLine(ambigous_zones_map,p1,p2,cvScalar(0,255,0),1);
	//	}
	//}

	for( int node_index = 0; node_index < (int) stroke_graph.size(); node_index++)
	{
		map<StrokeNode*,pair<CvPoint,CvPoint>>::iterator ite;
		for (ite = stroke_graph[node_index]->starting_pointers.begin(); ite != stroke_graph[node_index]->starting_pointers.end(); ite++)
		{
			CvPoint p1 = (*ite).second.first;
			CvPoint p2 = (*ite).second.second;
			cvLine(ambigous_zones_map,p1,p2,cvScalar(0,255,0),1);
		}
	}

	cvSaveImage("stroke_map.png",ambigous_zones_map);
	cvReleaseImage(&ambigous_zones_map); 

	// Build stroke
	StrokeBuilder stroke_builder;
	stroke_builder.build_spwt(input_filename,stroke_graph);
	//stroke_builder.refine_stroke_graph(stroke_graph);
	stroke_builder.search_path(stroke_graph);
	for(int i = 0; i< (int) stroke_graph.size(); i++)
	{
		delete stroke_graph[i];
	}
	return 0;
}



